package org.framework.common.api.service.repository;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.log4j.Logger;
import org.framework.common.api.query.Criteria;
import org.framework.common.api.query.Criterion.Operator;
import org.framework.common.api.query.OrderBean;
import org.framework.common.api.query.QueryBean;
import org.framework.common.api.query.Restrictions;
import org.framework.common.api.support.model.PageModel;
import org.framework.common.api.support.model.QueryModel;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.repository.NoRepositoryBean;

/**
 * Spring Data JPA 自定义全局Dao
 * <p>描述: 默认实现</p>
 * <p>Company: Professional</p>
 * @author chanlong(陈龙)
 * @date 2016年1月23日 上午10:34:11
 * @version 1.0.2016
 */
@NoRepositoryBean
public class CommonRepositoryDefault<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements CommonRepositoryAware<T, ID> {

	static Logger logger = Logger.getLogger(CommonRepositoryDefault.class);
	
    //private final String entityName;
    private final Class<T> entityClass;
    private final EntityManager entityManager;
    
	public CommonRepositoryDefault(Class<T> domainClass, EntityManager em) {
		this(JpaEntityInformationSupport.getEntityInformation(domainClass, em), em);
	}

	public CommonRepositoryDefault(final JpaEntityInformation<T, ?> information, EntityManager em) {
		super(information, em);
		this.entityManager = em;
		this.entityClass = information.getJavaType();
		//this.entityName = information.getEntityName();
	}
	
	@Override
	@SuppressWarnings("unchecked")
	public PageModel<T> findByQuery(final String hql, final int offset, final int limit) throws Exception {
		Query query = entityManager.createQuery(hql);
		long count = 0;
		List<T> result = query.setFirstResult(offset).setMaxResults(limit).getResultList();
		return new PageModel<T>(count, result);
	}
	
	@Override
	public PageModel<T> findByQuery(QueryModel support) throws Exception {
		Class<T> entityClass = support.getClass4Name();
		CriteriaBuilder builder = entityManager.getCriteriaBuilder();
		CriteriaQuery<T> query = builder.createQuery(entityClass);
		Root<T> root = query.from(entityClass);
		
		// 条件
		Predicate condition = createCondition(builder, root, query, support);
		
		// 排序
		query.orderBy(createOrder(builder, root, support));
		
		// 查询
		List<T> result = createTypedQuery(query.where(condition), support).getResultList();
		
		// 总数
		long count = count(builder, condition);
		
		// 结果
		return new PageModel<T>(count, result);
	}
	
	// 创建条件
	private Predicate createCondition(final CriteriaBuilder builder, final Root<T> root, final CriteriaQuery<T> query, final QueryModel support){
		QueryBean[] andQueries = support.getQueryBeans4And();
		QueryBean[] orQueries = support.getQueryBeans4Or();
		
		Criteria<T> criteria = new Criteria<T>();
		// and
		if(andQueries != null){
			for (QueryBean bean : andQueries) {
				criteria.add(Restrictions.exec(bean), Operator.AND);
			}
		}
		// or
		if(orQueries != null){
			for (QueryBean bean : orQueries) {
				criteria.add(Restrictions.exec(bean), Operator.OR);
			}
		}
		
		return criteria.toPredicate(root, query, builder);
	}
	
	// 创建排序
	private List<Order> createOrder(final CriteriaBuilder builder, final Root<?> root, final QueryModel support){
		OrderBean[] orders = support.getOrderBeans();
		List<Order> expression = new ArrayList<Order>();
		for (OrderBean order : orders) {
			expression.add(order.getExpresion(builder, root));
		}
		return expression;
	}
	
	// 创建查询
	private TypedQuery<T> createTypedQuery(final CriteriaQuery<T> query, final QueryModel support){
		int offset = support.getOffset();
		int limit = support.getLimit();
		return entityManager.createQuery(query).setFirstResult(offset).setMaxResults(limit);
	}
	
	// 结果总数
	private long count(final CriteriaBuilder builder, final Predicate condition){
		CriteriaQuery<Long> query = builder.createQuery(Long.class);
		Root<T> root = query.from(entityClass);
		query.select(builder.count(root)).where(condition);
		return entityManager.createQuery(query).getSingleResult().longValue();
	}
}
